﻿using APP.BaseClass;
using OpenCvSharp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace APP.OpenCV
{


    public class OrigineNumberModel
    {
        public string ImagePath { get; set; }

        //public string OrigineNumber { get; set; }
        public int OrigineNumber { get; set; }

        public Point[][] Countours { get; set; }

        public Point[] BestMatchCountour { get; set; }


        public void GetBestMatchCountour()
        {

        }
    }

    public class OpenCVOCRHelper2
    {

        int N = 5;       //载入数字图片个数
        int thres = 130;    //二值化阀值
                            //int flag0 = 1;    //准确标志  1为不符合，0为符合
        int n_min = 80;       //识别数字轮廓长度的下限 单位（像素） 
        int n_max = 400;      //识别数字轮廓长度的上限
                              //数字轮廓的长度和宽度  单位（像素）
        int n_width_min = 5, n_width_max = 40;
        int n_height_min = 30, n_height_max = 50;
        // 数组成员之间的距离小于一个阀值视为一个数
        int n_width_n_min = 15, n_width_n_max = 40;

        string[] picture = { "0.jpg", "1.jpg", "2.jpg", "3.jpg", "4.jpg" };  //数字图片集  这里储存白底黑子的数字图片 0 - 4；
        List<OrigineNumberModel> OriginData = new List<OrigineNumberModel>();  //储存数字图片轮廓
        Point[][] GetImageContour(Mat srcIn, int flag = 0)
        {
            Mat src;
            Point[][] seq = null;           //储存图片轮廓信息
            int total = 0;    //轮廓总数
            int count = 0;

            src = new Mat(srcIn.Size(), MatType.CV_8UC1);

            //拷贝图像
            srcIn.CopyTo(src);

            //二值化图像
            Cv2.Threshold(src, src, thres, 255, ThresholdTypes.BinaryInv);
            //计算图像轮廓  0-只获取最外部轮廓  1-获取全部轮廓
            if (flag == 0)
            {
                Cv2.FindContours(src, out seq, out HierarchyIndex[] hierarchy, RetrievalModes.External, ContourApproximationModes.ApproxNone, new Point(0, 0));
                total = seq.Length;
            }
            if (flag == 1)
            {
                Cv2.FindContours(src, out seq, out HierarchyIndex[] hierarchy, RetrievalModes.CComp, ContourApproximationModes.ApproxNone, new Point(0, 0));
                total = seq.Length;
            }

            //  printf("total = %d\n",total);
            //释放图像空间
            src.Release();
            //返回轮廓信息
            return seq;
        }
        //数字图片轮廓计算
        void Init()
        {
            Mat src0;
            Mat src = new Mat();
            int i;
            var folder = @"C:\repo\Play\MinesweeperAutoScanDemo\APP.OpenCV\Resources\";
            for (i = 0; i < 5; i++)
            {
                var item = new OrigineNumberModel();
                item.ImagePath = folder + picture[i];
                item.OrigineNumber = i;
                src0 = Cv2.ImRead(item.ImagePath, ImreadModes.Grayscale);

                src = src0.Clone();

                item.Countours = GetImageContour(src, 0);   //只获取最外部轮廓
                item.GetBestMatchCountour();
                OriginData.Add(item);
            }
        }

        int ReadNumber(Point[] contoursTemp)
        {
            int i;
            double tmp = 5, min = 5;
            int num = -1;

            var find = OriginData.FirstOrDefault(x => x.OrigineNumber == num);

            foreach (var item in OriginData)
            {
                tmp = Cv2.MatchShapes(contoursTemp, item.BestMatchCountour, ShapeMatchModes.I1);   //匹配
                if (tmp < min)
                {
                    min = tmp;
                    num = item.OrigineNumber;
                }
            }

            return num;
        }
        void Paixu(int[][] numList, int count)    //将数字按横坐标从左往右排列
        {
            int i, j;  //循环
            int n = 0;  //融合后的数字个数
            int tem;
            int head = 0, tail = 1;
            int width = 0;   //两数字间的距离
            int[] newList = new int[] { 0 };  //数字融合后的新序列
            for (i = 0; i < count - 1; i++)
            {
                for (j = i + 1; j < count; j++)
                {
                    if (numList[i][1] > numList[j][1])
                    {
                        //交换坐标
                        tem = numList[i][1]; numList[i][1] = numList[j][1]; numList[j][1] = tem;
                        //交换数字
                        tem = numList[i][0]; numList[i][0] = numList[j][0]; numList[j][0] = tem;
                    }
                }
            }
            if (count == 0)
            {
                Console.WriteLine("no number!");
            }
            else
            {
                for (i = 0; i < count; i++)
                {
                    Console.WriteLine($"numList[i][0]\t");
                }
            }
            //数字融合，可以自己改。。。。数字从左往右的顺序都在numList里面，[0]为数,[1]为坐标，自己可以根据数字间的距离判断是否为一个数
            if (count == 2)
            {
                width = numList[1][1] - numList[0][1];
                if ((width < n_width_n_max) && (width > n_width_n_min))
                {
                    tem = numList[0][0] * 10 + numList[1][0];
                    newList[0] = tem;
                }
                Console.WriteLine($"the number is {newList[0]}\t");
            }



        }


        public class resultModel
        {
            /// <summary>
            /// 横坐标
            /// </summary>
            public int X { get; set; }
            public Point Point { get; set; }

            /// <summary>
            /// 数字
            /// </summary>
            public int Number { get; set; }

            public Rect Rect { get; set; }
        }

        //数值比较
        public FeedBack<int> MatchNumber(string imagePath)
        {
            FeedBack<int> result = new FeedBack<int>();
            int travel = 1;  //如果为0，识别中间数字,如果为1，识别右边数字.其他数字，全部识别
            int i;  //循环标志
            int count = 0;   //数字轮廓个数
            int num = -1;   //识别一幅图像中的一个数字
            List<resultModel> numList = new List<resultModel>();  //一幅图像中的数字序列  一维是数字，二维是数字所在横坐标
            Point pt1, pt2;
            Rect ins;

            Init();   //初始化,在pic中储存所有图片轮廓

            Mat img = Cv2.ImRead(imagePath, ImreadModes.Grayscale);
            //if (travel == 0)
            //{
            //    ins.X = 170;
            //    ins.Y = 140;
            //    ins.Width = 300;
            //    ins.Height = 200;
            //    img.AdjustROI(ins.Top, ins.Bottom, ins.Left, ins.Right);
            //    //cvSetImageROI(img, ins);
            //}
            //if (travel == 1)
            //{
            //    ins.X = 470;
            //    ins.Y = 140;
            //    ins.Width = 165;
            //    ins.Height = 200;
            //    img.AdjustROI(ins.Top, ins.Bottom, ins.Left, ins.Right);
            //    //cvSetImageROI(img, ins);
            //}

            Mat imgColor = new Mat(img.Size(), MatType.CV_8UC3);
            Mat contoursImage = new Mat(img.Size(), MatType.CV_8UC1);

            /*
                    //对图像进行二值化
                    cvThreshold(img,img,100,255,CV_THRESH_BINARY);
                    //img的备份
                    cvCvtColor(img,imgColor,CV_GRAY2BGR);
            */

            /*
                    // 提取图像img的轮廓信息 contours指向第一个轮廓
                    int total = cvFindContours( img, storage, &contours, sizeof(CvContour),
                                CV_RETR_CCOMP, CV_CHAIN_APPROX_NONE, cvPoint(0,0) );
            */
            var contours = GetImageContour(img, 1);   //获取轮廓信息
            var contoursTemp = contours;

            //对轮廓进行循环
            foreach (var itemContours in contoursTemp)
            {


                //如果符合数字轮廓长度条件 保留并画出
                if (itemContours.Length > n_min && itemContours.Length < n_max)
                {
                    num = -1;
                    Rect rect = Cv2.BoundingRect(itemContours);  //根据序列，返回轮廓外围矩形
                    if ((rect.Width >= n_width_min && rect.Width <= n_width_max) && (rect.Height >= n_height_min && rect.Height <= n_height_max))
                    {
                        //匹配该轮廓数字
                        num = ReadNumber(itemContours);
                        //计算矩形顶点
                        pt1.X = rect.X;
                        pt1.Y = rect.Y;
                        pt2.X = rect.X + rect.Width;
                        pt2.Y = rect.Y + rect.Height;
                        if (num >= 0)
                        {
                            numList.Add(new resultModel
                            {
                                Number = num,
                                X = rect.X,
                                Rect = rect,
                            });
                        }
                        //在原图上绘制轮廓外矩形
                        Cv2.Rectangle(imgColor, pt1, pt2, new Scalar(0, 255, 0), 2);
                        //提取外轮廓 上的所以坐标点
                        for (i = 0; i < itemContours.Length; i++)
                        {
                            var pt = itemContours[i]; // 读出第i个点。

                            var vec3b_hsv = contoursImage.At<Vec3b>(pt.Y, pt.X);
                            contoursImage.Set<Vec3b>(pt.Y, pt.X, new Vec3b { Item1 = 255 });
                            //cvSetReal2D(contoursImage, pt->y, pt->x, 255.0);
                            //cvSet2D(imgColor, pt->y, pt->x, new Scalar(0, 0, 255, 0));
                        }
                        count++;    //数字轮廓+1
                    }
                }

            }



            for (i = 0; i < count; i++)
            {
                Console.WriteLine($"{ numList[i].Number}({numList[i].X})\t");
            }
            Cv2.NamedWindow("image", WindowFlags.AutoSize);
            Cv2.ImShow("image", imgColor);
            Cv2.NamedWindow("contours");
            Cv2.ImShow("contours", contoursImage);
            Cv2.WaitKey(0);

            //Cv2.ResetImageROI(imgColor);
            //Cv2.ResetImageROI(img);
            img.Release();
            contoursImage.Release();
            imgColor.Release();


            return result;
        }
    }
}
